package com.webdrider.manager.action.snippet;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.common.dao.interfaces.IDao;
import com.common.dao.util.Criteria;
import com.common.dao.util.QueryOperation;
import com.common.manager.abstracts.AbstractManager;
import com.common.security.exceptions.DatabaseException;
import com.common.security.exceptions.ManagerException;
import com.webdrider.dao.action.snippet.ArticleDao;
import com.webdrider.dao.interfaces.IArticleDao;
import com.webdrider.manager.interfaces.IArticleManager;
import com.webdrider.manager.interfaces.IArticleTagMapManager;
import com.webdrider.manager.interfaces.IDomainManager;
import com.webdrider.manager.interfaces.ITagManager;
import com.webdrider.model.action.snippet.Article;
import com.webdrider.model.action.snippet.ArticleTagMap;
import com.webdrider.model.action.snippet.Domain;
import com.webdrider.model.action.snippet.Tag;
import com.webdrider.security.exceptions.SnippetException;
import com.webdrider.util.Sorter;

public class ArticleManager extends AbstractManager<Article> implements IArticleManager {

	private IArticleDao articleDao;
	
	public ArticleManager() {
		articleDao = new ArticleDao();
	}
	
	@Override
	protected IDao<Article> getDao() {
		return articleDao;
	}
	
	@Override
	public List<Article> getAll() throws ManagerException {
		try {
			List<Article> articles = articleDao.getAll();
			List<Long> articleIds = new ArrayList<Long>();
			
			for (Article article : articles) {
				articleIds.add(article.getId());
			}
			
			IArticleTagMapManager mapManager = new ArticleTagMapManager();
			IDomainManager domainManager = new DomainManager();
			
			Map<Long, Domain> domains = new HashMap<Long, Domain>();
			Map<Long, List<Tag>> tags = mapManager.getAllTagsByArticleIds(articleIds);
			
			for (Article article : articles) {
				article.setTags(tags.get(article.getId()));
				
				if (domains.containsKey(article.getDomainId())) {
					article.setDomain(domains.get(article.getDomainId()));
				} else {
					Domain domain = domainManager.getDomain(article.getDomainId());
					domains.put(domain.getId(), domain);
					article.setDomain(domain);
				}
			}
			
			return articles;
		} catch (DatabaseException e) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}
	
	@Override
	public void save(Article article) throws ManagerException {
		try {
			ITagManager tagManager = new TagManager();
			IArticleTagMapManager mapManager = new ArticleTagMapManager();
			
			for (Tag tag : article.getTags()) {
				if (!tagManager.exist(tag.getValue())) {
					tagManager.save(tag);
				}
			}

			articleDao.save(article);
			Article resultArticle = getArticle(article.getTitle(), article.getDomainId());
			
			if (resultArticle != null) {
				for (Tag tag : article.getTags()) {
					Tag resultTag = tagManager.getTag(tag.getValue());
					ArticleTagMap map = new ArticleTagMap();
					
					map.setArticleId(resultArticle.getId());
					map.setTagId(resultTag.getId());
					
					mapManager.save(map);
				}
				
			} else {
				throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
			}
		} catch (DatabaseException databaseException) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		} catch (SnippetException articleException) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}
	
	private Article getArticle(String title, Long domainId) throws ManagerException, SnippetException {
		try {
			List<Criteria> criterias = new ArrayList<Criteria>();
			criterias.add(new Criteria(Article.TITLE, title, QueryOperation.EQUALS, true));
			criterias.add(new Criteria(Article.DOMAIN_ID, domainId, QueryOperation.EQUALS, false));
			
			List<Article> articles = articleDao.getByCriteria(criterias);
			Article article;

			if (articles.size() == 0) {
				throw new SnippetException(SnippetException.ARTICLE_NOT_FOUND);
			} else if (articles.size() == 1) {
				article = articles.get(0);
			} else {
				throw new SnippetException(SnippetException.MULTIPLE_ARTICLES_FOUND);
			}
			
			IArticleTagMapManager mapManager = new ArticleTagMapManager();
			IDomainManager domainManager = new DomainManager();
			
			List<Tag> tags =  mapManager.getTagsByArticleId(article.getId());
			Domain domain = domainManager.getById(domainId);
			
			article.setTags(tags);
			article.setDomain(domain);
			
			return article;
		} catch (DatabaseException e) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}

	@Override
	public List<Article> getAllByDomain(Domain domain) throws ManagerException, SnippetException {
		try {
			List<Criteria> criterias = new ArrayList<Criteria>();
			criterias.add(new Criteria(Article.DOMAIN_ID, domain.getId(), QueryOperation.EQUALS, false));
			List<Article> articles = articleDao.getByCriteria(criterias);
			List<Long> articleIds = new ArrayList<Long>();
			
			for(int i = 0; i < articles.size(); i++) {
				articleIds.add(articles.get(i).getId());
			}
			
			IArticleTagMapManager mapManager = new ArticleTagMapManager();
			Map<Long, List<Tag>> tags = mapManager.getAllTagsByArticleIds(articleIds);
			
			for (int i = 0; i < articles.size(); i++) {
				articles.get(i).setTags(tags.get(articles.get(i).getId()));
				articles.get(i).setDomain(domain);
			}
			
			return articles;
		} catch (DatabaseException e) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		} catch (ManagerException managerException) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}

	@Override
	public List<Article> getAllByTags(List<Tag> tags) throws ManagerException {
		try {
			IArticleTagMapManager mapManager = new ArticleTagMapManager();
			IDomainManager domainManager = new DomainManager();
			
			Map<Long, Long> articlesCount = mapManager.getAllArticleIdsCountByTagId(tags);
			List<Long> orderedArticles = Sorter.sortKeysByValue(articlesCount);
			List<Long> articleIds = new ArrayList<Long>(articlesCount.keySet());
			Map<Long, List<Tag>> articleTags = mapManager.getAllTagsByArticleIds(articleIds);
			List<Article> result = new ArrayList<Article>();
			
			for (int i = 0; i < orderedArticles.size(); i++) {
				Article article = articleDao.getById(orderedArticles.get(i));
				Domain domain = domainManager.getById(article.getDomainId());
				
				article.setTags(articleTags.get(article.getId()));
				article.setDomain(domain);
				
				result.add(article);
			}
			
			return result;
		} catch (ManagerException managerException) {
			throw managerException;
		} catch (DatabaseException e) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}

	@Override
	public List<Article> getAllByTagsAndDomain(List<Tag> tags, Domain domain) throws ManagerException {
		try {
			IArticleTagMapManager mapManager = new ArticleTagMapManager();
			//TODO Trivial priority: clean the code, it's messy
			Map<Long, Long> articlesCount = mapManager.getAllArticleIdsCountByTagId(tags);
			List<Long> orderedArticles = Sorter.sortKeysByValue(articlesCount);
			List<Long> articleIds = new ArrayList<Long>(articlesCount.keySet());
			Map<Long, List<Tag>> articleTags = mapManager.getAllTagsByArticleIds(articleIds);
			List<Article> result = new ArrayList<Article>();
			
			for (int i = 0; i < orderedArticles.size(); i++) {
				Article article = articleDao.getById(orderedArticles.get(i));
				
				if (article.getDomainId().equals(domain.getId())) {
					article.setTags(articleTags.get(article.getId()));
					article.setDomain(domain);
					
					result.add(article);
				}
			}
			
			return result;
		} catch (ManagerException managerException) {
			throw managerException;
		} catch (DatabaseException e) {
			throw new ManagerException(ManagerException.DATABASE_EXCEPTION);
		}
	}
}